/**
 * Copyright © 2010 DocuLibre inc.
 * 
 * This file is part of Constellio.
 * 
 * Constellio is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Constellio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Constellio.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.doculibre.constellio.connector.documents;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;

import com.google.enterprise.connector.spi.Property;
import com.google.enterprise.connector.spi.RepositoryException;
import com.google.enterprise.connector.spi.Value;

/**
 * http://code.google.com/apis/searchappliance/documentation/connectors/110/
 * connector_dev/cdg_traversing.html#mp
 * 
 * @author admin
 * 
 */
public class SmartProperty implements Property {
	private static Value fromObject(Object obj) {
		if (obj == null)
			return null;
		if ((obj instanceof Integer) || (obj instanceof Long)
				|| (obj instanceof Short))
			return Value.getLongValue(((Number) obj).longValue());
		else if ((obj instanceof Float) || (obj instanceof Double))
			return Value.getDoubleValue(((Number) obj).doubleValue());
		else if (obj instanceof Boolean)
			return Value.getBooleanValue(((Boolean) obj).booleanValue());
		else if (obj instanceof String)
			return Value.getStringValue((String) obj);
		else if (obj instanceof InputStream)
			return Value.getBinaryValue((InputStream) obj);
		else if (obj instanceof File)
			try {
				return Value.getBinaryValue(new FileInputStream((File) obj));
			} catch (FileNotFoundException e) {
				throw new RuntimeException(e);
			}
		else if (obj instanceof Calendar)
			return Value.getDateValue((Calendar) obj);
		else if (obj instanceof Date) {
			Calendar calendar = new GregorianCalendar();
			calendar.setTime((Date) obj);
			return Value.getDateValue(calendar);
		} else {
			// Fall through to providing a String representation of
			// non-primitive objects.
			return Value.getStringValue(obj.toString());
		}
	}

	// Used by single-value properties.
	Value singleValue = null;

	// Used by multi-value properties.
	Iterator<?> iterator = null;

	private Collection<?> collection = null;
	
	public SmartProperty(Object obj) {
		// Use the Value class to construct a boolean value.
		singleValue = fromObject(obj);
	}
	public SmartProperty(boolean aBool) {
		// Use the Value class to construct a boolean value.
		singleValue = Value.getBooleanValue(aBool);
	}

	public SmartProperty(Boolean aBool) {
		// Use the Value class to construct a boolean value.
		singleValue = Value.getBooleanValue(aBool.booleanValue());
	}

	public SmartProperty(byte[] byteArray) {
		// Use the Value class to construct a byteArray binary value.
		singleValue = Value.getBinaryValue(byteArray);
	}

	public SmartProperty(Calendar aDate) {
		// Use the Value class to construct a Date value.
		singleValue = Value.getDateValue(aDate);
	}

	// Constructors for multi-valued Properties.
	public SmartProperty(Collection<?> values) {
		this.iterator = values.iterator();
		this.collection = values;
	}

	public SmartProperty(Date aDate) {
		// Use the Value class to construct a Date value.
		Calendar calendar = new GregorianCalendar();
		calendar.setTime(aDate);
		singleValue = Value.getDateValue(calendar);
	}

	public SmartProperty(double aDouble) {
		// Use the Value class to construct a floating point value.
		singleValue = Value.getDoubleValue(aDouble);
	}

	public SmartProperty(Double aDouble) {
		// Use the Value class to construct a floating point value.
		singleValue = Value.getDoubleValue(aDouble.doubleValue());
	}

	public SmartProperty(float aFloat) {
		// Use the Value class to construct a floating point value.
		singleValue = Value.getDoubleValue((double) aFloat);
	}

	public SmartProperty(Float aFloat) {
		// Use the Value class to construct a floating point value.
		singleValue = Value.getDoubleValue(aFloat.doubleValue());
	}

	public SmartProperty(InputStream aStream) {
		// Use the Value class to construct a binary InputStream value.
		singleValue = Value.getBinaryValue(aStream);
	}
	
	public SmartProperty(File file) {
		// Use the Value class to construct a binary InputStream value.
		try {
			singleValue = Value.getBinaryValue(new FileInputStream(file));
		} catch (FileNotFoundException e) {
			throw new RuntimeException(e);
		}
	}

	// Constructors for single-value Properties of primitive types.
	public SmartProperty(int anInt) {
		// Use the Value class to construct a long integer value.
		singleValue = Value.getLongValue((long) anInt);
	}

	// Constructors for single-value Properties of Java primitive wrapper
	// Objects.
	public SmartProperty(Integer anInt) {
		// Use the Value class to construct a long integer value.
		singleValue = Value.getLongValue(anInt.longValue());
	}

	public SmartProperty(long aLong) {
		// Use the Value class to construct a long integer value.
		singleValue = Value.getLongValue(aLong);
	}

	public SmartProperty(Long aLong) {
		// Use the Value class to construct a long integer value.
		singleValue = Value.getLongValue(aLong.longValue());
	}

	public SmartProperty(Object[] objectArray) {
		this.iterator = Arrays.asList(objectArray).iterator();
		this.collection = Arrays.asList(objectArray);
	}

	public SmartProperty(Short aShort) {
		// Use the Value class to construct a long integer value.
		singleValue = Value.getLongValue(aShort.longValue());
	}

	public SmartProperty(SmartProperty smartProperty) {
		if (smartProperty.getCollection() != null) {
			this.iterator = smartProperty.getCollection().iterator();
		}
		this.singleValue = smartProperty.singleValue;
	}

	public SmartProperty(String aString) {
		// Use the Value class to construct a String value.
		singleValue = Value.getStringValue(aString);
	}

	public Collection<?> getCollection() {
		return this.collection;
	}

	@Override
	public Value nextValue() throws RepositoryException {
		if (iterator != null) {
			// Return the values of a Property with multiple values.
			if (iterator.hasNext()) {
				return fromObject(iterator.next());
			}
			return null;
		} else {
			// Return the value of a Property with a single value, but only
			// once;
			Value returnValue = singleValue;
			singleValue = null;
			return returnValue;
		}
	}

}